﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace ftp_server
{
    public partial class Frm_server : Form
    {
        private Socket listener;//监听的socket
        private Dictionary<string, Socket> clients=new Dictionary<string, Socket>();//地址与cmd_socket的映射
        private Dictionary<string, int> login_status = new Dictionary<string, int>();//登录状态
        private Dictionary<string, string> names = new Dictionary<string, string>();//地址与用户名的映射
        private Dictionary<string, string> cur_dir = new Dictionary<string, string>();//现在的路径
        private Dictionary<string, Socket> data = new Dictionary<string, Socket>();//地址与数据socket的映射
        private Dictionary<string, Socket> data_listener = new Dictionary<string, Socket>();//数据传输监听
        private Dictionary<string, bool> Is_PASV = new Dictionary<string, bool>();//是否被动连接
        private Dictionary<string, bool> Is_binary = new Dictionary<string, bool>();//是否是二进制传输
        private string Share_Path;//共享路径
        public AddUser add = new AddUser();
        private string Curr_Time
        {
            get
            {
                return DateTime.Now.ToString() + Environment.NewLine;
            }
        }
        public Frm_server()
        {
            InitializeComponent();
            add.Users.Add("user", "arsenal666");
        }
        private void ReplyToClient(Socket client,string msg)
        {
            UpdateMessage(msg);
            byte[] cmdBytes = Encoding.GetEncoding("utf-8").GetBytes((msg + "\r\n").ToCharArray());
            client.Send(cmdBytes, cmdBytes.Length, 0);
        }
        private void button1_Click(object sender, EventArgs e)
        {
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iPEnd = new IPEndPoint(IPAddress.Parse(this.textBox1.Text.Trim()),int.Parse(this.textBox2.Text.Trim()));
            try
            {
                listener.Bind(iPEnd);
                listener.Listen(int.Parse(this.textBox3.Text.Trim()));
                MessageBox.Show("服务器已启动");
                UpdateMessage("服务器开启成功");
            }
            catch(Exception ex)
            {
                MessageBox.Show("服务器启动异常"+ex.Message);
                return;
            }
            //监听线程
            Task.Run(new Action(()=> ListenConnection()));
        }

        private void ListenConnection()//连接监听
        {
            while(true)
            {
                Socket Client =listener.Accept();
                addUserList(Client.RemoteEndPoint.ToString(), true);
                this.clients.Add(Client.RemoteEndPoint.ToString(), Client);
                login_status.Add(Client.RemoteEndPoint.ToString(), 0);
                cur_dir.Add (Client.RemoteEndPoint.ToString(), Share_Path);
                Is_binary.Add(Client.RemoteEndPoint.ToString(), true);
                Task.Run(() => RecieveMsg(Client));
            }
        }

        private void RecieveMsg(Socket client)//监听处理客户端需求
        {
            string info;
            string sendString1 = "220 FTP Server v1.0";//建立连接
            ReplyToClient(client, sendString1);
            while (true)
            {
                info = null;
                byte[] buffer = new byte[1024];
                int length;
                try
                {
                    length = client.Receive(buffer);
                    if (length > 0)
                    {
                        info+= Encoding.GetEncoding("utf-8").GetString(buffer, 0, length);
                        UpdateMessage(info);
                    }
                }catch(Exception ex)
                {
                    //客户端离开
                    string ip1 = client.RemoteEndPoint.ToString();
                    addUserList(ip1, false);//第二个参数为false表示删除用户
                    clients.Remove(ip1);
                    login_status.Remove(ip1);
                    cur_dir.Remove(ip1);
                    data.Remove(ip1);
                    break;
                }
                if(info==null||info==" ")
                {
                    UpdateMessage("接受信息为空");
                    break;
                }
                UpdateMessage(string.Format("来自{0}：[{1}]", client.RemoteEndPoint.ToString(), info));
                string command = info;
                string param = string.Empty;
                string ip = client.RemoteEndPoint.ToString();
                int index = info.IndexOf(' ');
                if (index != -1)
                {
                    command = info.Substring(0, index).ToUpper();
                    param = info.Substring(command.Length).Trim();
                }
                if(command=="QUIT")
                {
                    addUserList(ip, false);
                    return;
                }
                else
                {
                    switch (login_status[ip])
                    {
                        case 0: cmdUSER(client, command, param); break;
                        case 1: cmdPASSWORD(client, command, param);break;
                        case 2:
                            {
                                switch (command.Trim())
                                {
                                    case "CWD":
                                        cmdCWD(client, param);
                                        break;
                                    case "PWD":
                                        cmdPWD(client);
                                        break;
                                    case "PASV":
                                        cmdPASV(client);
                                        break;
                                    case "PORT":
                                        cmdPORT(client, param);
                                        break;
                                    case "LIST":
                                        cmdLIST(client, param);
                                        break;
                                    case "NLIST":
                                        cmdLIST(client, param);
                                        break;
                                    // 处理下载文件命令
                                    case "RETR":
                                        cmdRETR(client, param);
                                        break;
                                    // 处理上传文件命令
                                    case "STOR":
                                        cmdSTOR(client, param);
                                        break;
                                    // 处理删除命令
                                    case "DELE":
                                        cmdDELE(client, param);
                                        break;
                                    // 使用Type命令在ASCII和二进制模式进行变换
                                    case "TYPE":
                                        cmdTYPE(client, param);
                                        break;
                                    case "SIZE":
                                        cmdSIZE(client,param);
                                        break;
                                    default:
                                        string sendString = "502 command is not implemented.";
                                        ReplyToClient(client, sendString);
                                        break;
                                }//判断指令类型分别进行处理，过长，不一一展示

                                break;
                            }
                    }
                }
            }
        }
        private void cmdUSER(Socket client,string command,string param)
        {
            string sendString = string.Empty;
            string ip = client.RemoteEndPoint.ToString();
            if (command == "USER")
            {
                sendString = "331 USER command OK, password required.";
                names.Add(ip, param.Trim());
                // 设置loginOk=1为了确保后面紧接的要求输入密码
                // 1表示已接收到用户名，等到接收密码
                login_status[ip] = 1;
            }
            else
            {
                sendString = "501 USER command syntax error.";
            }

            ReplyToClient(client, sendString);
        }
        private void cmdPASSWORD(Socket client, string command, string param)
        {
            string sendString = string.Empty;
            //MessageBox.Show(param);
            string ip = client.RemoteEndPoint.ToString();
            if (command == "PASS")
            {
                string password = null;
                if (add.Users.ContainsKey(names[ip]))
                {
                    password = add.Users[names[ip]];
                    if (password == param.Trim()||names[ip]=="anonymous")
                    {
                        sendString = "230 User logged in success";
                        // 2表示登录成功
                        login_status[ip] = 2;
                    }
                    else
                    {
                        sendString = "530 Password incorrect.";
                    }
                }
                else
                {
                    sendString = "530 User name or password incorrect.";
                }
            }
            else
            {
                sendString = "501 PASS command Syntax error.";
            }

            ReplyToClient(client, sendString);
        }
        private void cmdCWD(Socket client, string param)
        {
            string sendString = string.Empty;
            string ip = client.RemoteEndPoint.ToString();
            try
            {
                string dir = null;
                if (param.Contains("C"))
                    dir = param;
                else dir = Share_Path.TrimEnd('\\') + param;
                // 是否为当前目录的子目录，且不包含父目录名称
                if (Directory.Exists(dir))
                {
                    cur_dir[ip] = dir;
                   // MessageBox.Show(dir);
                    sendString = "250 Directory changed to '" + dir + "' successfully";
                }
                else
                {
                    sendString = "550 Directory '" + dir + "' does not exist";
                }
            }
            catch
            {
                sendString = "502 Directory changed unsuccessfully";
            }

            ReplyToClient(client, sendString);
        }
        private void cmdPWD(Socket client)
        {
            string sendString = string.Empty;
            sendString = "257 '" + cur_dir[client.RemoteEndPoint.ToString()] + "' is the current directory";
            ReplyToClient(client, sendString);
        }
        private void cmdPORT(Socket client, string param)
        {

        }
        private void cmdSIZE(Socket client, string filename)
        {
            string sendString = string.Empty;
            string ip = client.RemoteEndPoint.ToString();
            string path = cur_dir[ip] + @"\" + filename;
            long lSize = 0;
            if (File.Exists(path))
            {
                lSize = new FileInfo(path).Length;
                sendString = "213 " + lSize.ToString();
            }
            else
            {
                sendString = "502 文件不存在";
            }
            ReplyToClient(client, sendString);

        }
        private void cmdPASV(Socket client)
        {

            string sendString = string.Empty;
            string ip = client.RemoteEndPoint.ToString();
            string[] ip_str = "127.0.0.1".Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            byte[] b = new byte[4];
            for (int i = 0; i < 4; i++)
            {
                b[i] = Convert.ToByte(ip_str[i]);
            }
            IPAddress localip = new IPAddress(b);
            // 被动模式，即服务器接收客户端的连接请求
            // 被动模式下FTP服务器使用随机生成的端口进行传输数据
            // 而主动模式下FTP服务器使用端口20进行数据传输
            Random random = new Random();
            int random1, random2;
            int port;
            while (true)
            {
                // 随机生成一个端口进行数据传输
                random1 = random.Next(5, 200);
                random2 = random.Next(0, 200);
                // 生成的端口号控制>1024的随机端口
                // 下面这个运算算法只是为了得到一个大于1024的端口值
                port = random1 << 8 | random2;
                try
                {
                    data_listener[ip] = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    data_listener[ip].Bind(new IPEndPoint( localip, port));
                    data_listener[ip].Listen(10);
                    UpdateMessage("TCP 数据连接已打开（被动模式）--" + localip.ToString() + "：" + port);
                }
                catch
                {
                    //MessageBox.Show(".");
                    continue;
                }

                Is_PASV[ip] = true;
                string temp = localip.ToString().Replace('.', ',');

                // 必须把端口号IP地址告诉客户端，客户端接收到响应命令后，
                // 再通过新的端口连接服务器的端口P，然后进行文件数据传输
                sendString = "227 Entering Passive Mode(" + temp + "," + random1 + "," + random2 + ")";
                ReplyToClient(client, sendString);
                break;
            }
        }
        private void cmdLIST(Socket client,string param)
        {
            string sendString = string.Empty;
            string ip = client.RemoteEndPoint.ToString();
            // 得到目录列表
            string[] dir = Directory.GetDirectories(cur_dir[ip]);
            if (string.IsNullOrEmpty(param) == false)
            {
                if (Directory.Exists(cur_dir[ip] + param))
                {
                    dir = Directory.GetDirectories(cur_dir[ip] + param);
                    cur_dir[ip] = cur_dir[ip] + param;
                }
                else
                {
                    string s = cur_dir[ip].TrimEnd('\\');
                    cur_dir[ip] = param;
                    // MessageBox.Show(cur_dir[ip]);
                }
            }
            for (int i = 0; i < dir.Length; i++)
            {
                string folderName = Path.GetFileName(dir[i]);
                DirectoryInfo d = new DirectoryInfo(dir[i]);
                // 按下面的格式输出目录列表
                sendString += "dwr-\t " + folderName +Environment.NewLine;
            }

            // 得到文件列表
            string[] files = Directory.GetFiles(cur_dir[ip]);
            if (string.IsNullOrEmpty(param) == false)
            {
                if (Directory.Exists(cur_dir[ip] + param + "\\"))
                {
                    files = Directory.GetFiles(cur_dir[ip] + param + "\\");
                }
            }
            for (int i = 0; i < files.Length; i++)
            {
                FileInfo f = new FileInfo(files[i]);
                string fileName = Path.GetFileName(files[i]);
                // 按下面的格式输出文件列表
                sendString += "-wr-\t " +fileName + Environment.NewLine;
            }

            
            bool isBinary = Is_binary[ip];
            Is_binary[ip] = false;
            ReplyToClient(client, "150 Opening ASCII data connection");
            InitDataSocket(client);
            SendBytes(client, sendString);
            ReplyToClient(client, "226 Transfer complete");
            Is_binary[ip] = isBinary;
        }
        private void cmdRETR(Socket client, string filename)
        {
            string sendString ;
            string ip = client.RemoteEndPoint.ToString();
            // 下载的文件全名
            string path = cur_dir[ip] +@"\"+ filename;
            FileStream filestream = new FileStream(path, FileMode.Open, FileAccess.Read);

            // 发送150到用户，表示服务器文件状态良好，将要打开数据连接传输文件
            if (Is_binary[ip])
            {
                sendString = "150 Opening BINARY mode data connection for download";
            }
            else
            {
                sendString = "150 Opening ASCII mode data connection for download";
            }

            ReplyToClient(client, sendString);
            InitDataSocket(client);
            SendFileByUserSession(client, filestream);
            ReplyToClient(client, "226 Transfer complete");
        }
        private void cmdSTOR(Socket client, string filename)
        {
            string sendString = "";
            string ip = client.RemoteEndPoint.ToString();
            // 上传的文件全名
            string path = cur_dir[ip]+"\\"+ filename;
            FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);

            // 发送150到用户，表示服务器状态良好
            if (Is_binary[ip])
            {
                sendString = "150 Opening BINARY mode data connection for upload";
            }
            else
            {
                sendString = "150 Opeing ASCII mode data connection for upload";
            }

            ReplyToClient(client, sendString);
            InitDataSocket(client);
            ReaddFileByUserSession(client, fs);
            ReplyToClient(client, "226 Transfer complete");

        }
        private void cmdDELE(Socket client, string filename)
        {
            string sendString = "";
            string ip = client.RemoteEndPoint.ToString();
            // 删除的文件全名
            string path = cur_dir[ip] +"\\"+ filename;
            UpdateMessage("正在删除文件" + filename + "...");
            File.Delete(path);
            UpdateMessage("删除成功");
            sendString = "250 File " + filename + " has been deleted.";
            ReplyToClient(client, sendString);
        }
        private void cmdTYPE(Socket client, string param)
        {
            string sendstring = "";
            string ip = client.RemoteEndPoint.ToString();
            if (param == "I")
            {
                // 二进制
                Is_binary[ip] = true;
                sendstring = "220 Type set to I(Binary)";
            }
            else
            {
                // ASCII方式
                Is_binary[ip] = false;
                sendstring = "330 Type set to A(ASCII)";
            }

            ReplyToClient(client, sendstring);
        }
        private void InitDataSocket(Socket client)
        {
            Socket data_socket = null;
            string ip = client.RemoteEndPoint.ToString();
            if(Is_PASV[ip])
            {
                UpdateMessage(ip + "采取被动连接");
                data_socket = data_listener[ip].Accept();
            }
            else
            {
                UpdateMessage(ip + "采取被动方式连接");
                data_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                data_socket.Bind(client.RemoteEndPoint);
            }
            data[ip] = data_socket;
        }
        private void SendBytes(Socket client,string sendString)
        {
            UpdateMessage("向客户端发送字节信息");//是ASCII传输
            string ip = client.RemoteEndPoint.ToString();
            try
            {
                byte[] buffer = new byte[1024 * 1024 * 2];
                buffer = Encoding.GetEncoding("utf-8").GetBytes(sendString);
                data[ip].Send(buffer,buffer.Length,0);
                UpdateMessage("发送完毕");
            }
            finally
            {
                data[ip].Close();
            }
        }
        private void SendFileByUserSession(Socket client, FileStream fs)
        {
            UpdateMessage("向用户发送(文件流)：[...");
            string ip = client.RemoteEndPoint.ToString();
            try
            {
                if (Is_binary[ip])//是否以binary方式
                {
                    byte[] bytes = new byte[1024];
                    BinaryReader binaryReader = new BinaryReader(fs);
                    int count = binaryReader.Read(bytes, 0, bytes.Length);
                    BinaryWriter bin= new BinaryWriter(new NetworkStream(data[ip]), Encoding.UTF8); 
                    while (count > 0)
                    {
                        //data[ip].Send(bytes, count, 0);
                        bin.Write(bytes, 0, count);
                        bin.Flush();
                        count = binaryReader.Read(bytes, 0, bytes.Length);
                    }
                    bin.Close();
                    binaryReader.Close();
                }
                else
                {
                    StreamReader streamReader = new StreamReader(fs);
                    while (streamReader.Peek() > -1)
                    {
                        byte[] buffer = new byte[1024 * 1024 * 10];
                        buffer = Encoding.UTF8.GetBytes(streamReader.ReadLine());
                        data[ip].Send(buffer,buffer.Length,0);
                    }
                    streamReader.Close();
                }
                UpdateMessage("...]发送完毕！");
            }
            finally
            {
                data[ip].Close();
                fs.Close();
            }
        }
        private void ReaddFileByUserSession(Socket client, FileStream fs)
        {
            UpdateMessage("接收用户上传数据（文件流）：[...");
            string ip = client.RemoteEndPoint.ToString();
            try
            {
                if (Is_binary[ip])//是否以binary方式
                {
                    byte[] bytes = new byte[1024];
                    BinaryWriter binaryWriter = new BinaryWriter(fs);
                    BinaryReader binaryReader = new BinaryReader(new NetworkStream(data[ip]), Encoding.UTF8);
                    int count = binaryReader.Read(bytes, 0, bytes.Length);
                    while (count > 0)
                    {
                        binaryWriter.Write(bytes, 0, count);
                        binaryWriter.Flush();
                        count = binaryReader.Read(bytes, 0, bytes.Length);
                    }
                    binaryReader.Close();
                    binaryWriter.Close();
                }
                else
                {
                    StreamWriter streamWriter = new StreamWriter(fs);
                    StreamReader streamReader = new StreamReader(new NetworkStream(data[ip]), Encoding.UTF8);
                    while (streamReader.Peek() > -1)
                    {
                        streamWriter.Write(streamReader.ReadLine());
                        streamWriter.Flush();
                    }
                    streamReader.Close();
                    streamWriter.Close();
                }

                UpdateMessage("...]接收完毕");
            }
            finally
            {
                data[ip].Close();
                fs.Close();
            }
        }
        private void addUserList(string uid,bool add)
        {
            Invoke(new Action(() =>
            {
                if (add)
                {
                    this.List_User.Items.Add(uid);
                    UpdateMessage(uid + "连接到服务器");
                }
                else
                {
                    this.List_User.Items.Remove(uid);

                    UpdateMessage(uid + "离开服务器连接");
                }
            }));
        }
        private void UpdateMessage(string message)
        {
            Invoke(new Action(() =>
            {
                this.richTextBox1.AppendText(Curr_Time+message+Environment.NewLine);
            }));
        }

        private void button3_Click(object sender, EventArgs e)
        {
            {
                FolderBrowserDialog dilog = new FolderBrowserDialog();

                dilog.Description = "请选择root路径";

                if (dilog.ShowDialog() == DialogResult.OK || dilog.ShowDialog() == DialogResult.Yes)

                {
                    Share_Path = dilog.SelectedPath;
                    this.textBox4.Text = Share_Path;
                }

            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if(checkBox1.Checked&&!add.Users.ContainsKey("anonymous"))
            {
                add.Users.Add("anonymous", "");
            }
            else if(!checkBox1.Checked&&add.Users.ContainsKey("anonymous"))
            {
                add.Users.Remove("anonymous");
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
             add.Show();
        }
    }
}
